require 'fileutils'
require 'parallel'
require 'rbconfig'
require File.expand_path(File.join(File.dirname(__FILE__), 'download'))

module RubySwig
  SWIG_VERSION='2.0.7'
  PCRE_VERSION='8.30'
  IS_WINDOWS = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
  if (IS_WINDOWS)
    begin
      # Setup devkit path so we have access to bash and tar.
      require 'devkit'
    rescue LoadError
      puts "Devkit is required on Windows to build SWIG from source."
    end
  end

  PROJECT_ROOT=File.expand_path(File.join(File.dirname(__FILE__), '..'))
  EXT_ROOT=File.expand_path(File.dirname(__FILE__))
  SOURCE_DIR=File.join(EXT_ROOT, "swig-#{SWIG_VERSION}")
  INSTALL_DIR=File.join(PROJECT_ROOT, "swig")
  PWD=FileUtils.pwd
  GEMDIR=`gem env gemdir`.chomp
  GEM_CACHE_DIR=File.join(GEMDIR, 'cache')
  # Windows VC++: download http://prdownloads.sourceforge.net/swig/swigwin-2.0.7.zip

  class SwigBuilder
    def initialize
      if `bash -c "which bsdtar"`.empty?
        @tar_cmd='tar'
      else
        @tar_cmd='bsdtar'
      end
    end

    def bsd_tar?
      @tar_cmd=='bsdtar'
    end

    def system!(*args)
      system(*args)
      return_code=$?
      if return_code != 0 then
        raise RuntimeError.new("Failed to run #{args}. Return code: #{return_code}")
      end
    end

    def link(src, dest, options={})
      if IS_WINDOWS
        begin
          FileUtils.cp(src, dest)
        rescue Errno::EEXIST
          puts "File #{src} already exists, skipping file copy"
        end
      else
        begin
          FileUtils.ln_s(src, dest, options)
        rescue Errno::EEXIST
          puts "File #{src} already exists, skipping symlink"
        end
      end
    end

    def run_script!(command)
      if IS_WINDOWS
        system "bash -c '#{command}'"
      else
        system! command
      end
    end

    # Detect existing pcre library.
    def pcre_installed?
      system "gcc -c #{EXT_ROOT}/findpcre.c -o /dev/null"
      $? == 0
    end

    # Download and build local version of pcre.
    def install_pcre
      pcre_filename = "pcre-#{PCRE_VERSION}.tar.gz"
      pcre_download_path = File.join(GEM_CACHE_DIR, pcre_filename)
      pcre_url = "http://sourceforge.net/projects/pcre/files/pcre/#{PCRE_VERSION}/#{pcre_filename}"

      FileUtils.chdir(SOURCE_DIR)
      if not File.exists?(pcre_download_path) then
        Downloader.download_file(pcre_url, pcre_download_path)
        # system! "curl -L -o #{pcre_download_path} #{pcre_url}"
        # system! "wget -P #{GEM_CACHE_DIR} #{pcre_url}"
      end
      link(pcre_download_path, pcre_filename)
      if bsd_tar?
        FileUtils.cp(File.join(EXT_ROOT, 'bsdtar-pcre-build.sh'), '.')
        run_script! './bsdtar-pcre-build.sh'
      else
        run_script! 'Tools/pcre-build.sh'
      end
    end

    def install_manpages(dest)
      begin
        FileUtils.chdir(INSTALL_DIR)
        Dir.glob(File.join('share', 'man', '**', '*')).each do |file|
          dest_file = File.join(dest, file)
          FileUtils.mkdir_p(File.dirname(file))
          link(File.expand_path(file), dest_file, :force => true)
        end
      rescue Errno::EACCES
        puts "Warning: man pages are not installed. To install locally:"
        puts "cd #{PROJECT_ROOT}; sudo rake install_manpages"
      rescue Errno::EEXIST
        puts "Man pages already installed."
      rescue => e
        puts "Rescuing #{e.class}"
        puts e.inspect
        raise
      ensure
        FileUtils.chdir(PWD)
      end
    end

    def build_swig
      swig_filename = "swig-#{SWIG_VERSION}.tar.gz"
      swig_download_path = File.join(GEM_CACHE_DIR, swig_filename)
      swig_url = "http://downloads.sourceforge.net/project/swig/swig/swig-#{SWIG_VERSION}/#{swig_filename}"

      FileUtils.chdir(EXT_ROOT)
      if not File.exists?(swig_download_path) then
        Downloader.download_file(swig_url, swig_download_path)
        # system! "curl -L -o #{swig_download_path} #{swig_url}"
        # system! "wget -P #{GEM_CACHE_DIR} #{swig_url}"
      end
      link(swig_download_path, swig_filename)
      system! "#{@tar_cmd} -xf swig-#{SWIG_VERSION}.tar.gz"
      FileUtils.chdir(SOURCE_DIR)

      # Requirement: pcre.
      if not pcre_installed? then
        install_pcre
      end

      system! "make distclean" if File.exists?('Makefile')
      run_script! "./configure --prefix=#{INSTALL_DIR}"

      processors = Parallel.processor_count
      if processors > 1
        system! "make -j#{processors}"
      else
        system! 'make'
      end
      FileUtils.chdir(PWD)
    end

    def install_swig
      FileUtils.chdir(SOURCE_DIR)
      system! 'make install'
      FileUtils.chdir(PWD)
    end
  end
end

task :default => [:build_swig]

task :build_swig do
  builder = RubySwig::SwigBuilder.new
  builder.build_swig
  builder.install_swig
  builder.install_manpages('/usr/local')
end

task :install_manpages do
  RubySwig::SwigBuilder.new.install_manpages('/usr/local')
end

